/*
** Copyright (C) 2009 Joshua Brent Marsh <joshua@icub3d.com>
**  
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 3 of the License, or
** (at your option) any later version.
**  
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**  
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**  
*/

/* $Id: test_set.c 32 2009-10-28 22:51:19Z joshua@icub3d.com $ */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <malloc.h>

#include <CUnit/Basic.h>

#include "../src/set.h"
#include "../src/function_prototypes.h"

#include "test_helpers.h"
#include "test_set.h"

Set *first, *second;

int
init_test_set_suite ()
{
  first = set_create (&comp_char);
  if (first == NULL)
    return 1;

  second = set_create (&comp_char);
  if (second == NULL)
    {
      set_destroy (first, &dest_char);
      return 1;
    }

  return 0;
}

int
clean_test_set_suite ()
{
  set_destroy (first, &dest_char);
  set_destroy (second, &dest_char);

  return 0;
}

void
test_set_add ()
{
  char *i, *x;
  char failures[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
  int cur;

  /* Standard insert. */
  for (cur = 0; cur < 6; cur++)
    {
      i = malloc_char ('A' + cur);
      if (i == NULL)
	{
	  CU_FAIL ("malloc i failed.");
	  continue;
	}
      
      x = set_add (first, i);
      CU_ASSERT (x != NULL);
      CU_ASSERT (x == i);
    }

  /* Standard failures. */
  for (cur = 0; cur < 6; cur++)
    {
      x = set_add (first, &(failures[cur]));
      CU_ASSERT (x != NULL);
      CU_ASSERT (x != &(failures[cur]));
    }


  /* Standard insert. */
  for (cur = 4; cur < 10; cur++)
    {
      i = malloc_char ('A' + cur);
      if (i == NULL)
	{
	  CU_FAIL ("malloc i failed.");
	  continue;
	}
      
      x = set_add (second, i);
      CU_ASSERT (x != NULL);
      CU_ASSERT (x == i);
    }

  /* Standard failures. */
  for (cur = 4; cur < 10; cur++)
    {
      x = set_add (second, &(failures[cur]));
      CU_ASSERT (x != NULL);
      CU_ASSERT (x != &(failures[cur]));
    }
}

void
test_set_search ()
{
  char c;
  int cur;

  /* Standard finds. */
  for (cur = 0; cur < 10; cur++)
    {
      c = 'A' + cur;

      if (cur < 6)
	CU_ASSERT (*((char *)set_search (first, &c)) == ('A' + cur));

      if (cur >= 6)
	CU_ASSERT (((char *)set_search (first, &c)) == NULL);

      if (cur >= 4)
	CU_ASSERT (*((char *)set_search (second, &c)) == ('A' + cur));

      if (cur < 4)
	CU_ASSERT (((char *)set_search (second, &c)) == NULL);
    }

  for (cur = 0; cur < 10; cur++)
    {
      c = 'L' + cur;

      CU_ASSERT (set_search (first, &c) == NULL);
      CU_ASSERT (set_search (second, &c) == NULL);
    }

}

void
test_set_remove ()
{
  char *c, x;

  x = 'A';
  c = set_remove (first, &x);
  CU_ASSERT (c != NULL);
  CU_ASSERT (*c == 'A');
  free (c);

  x = 'E';
  c = set_remove (first, &x);
  CU_ASSERT (c != NULL);
  CU_ASSERT (*c == 'E');
  free (c);

  x = 'Z';
  c = set_remove (first, &x);
  CU_ASSERT (c == NULL);

  x = 'M';
  c = set_remove (first, &x);
  CU_ASSERT (c == NULL);

  x = 'Q';
  c = set_remove (first, &x);
  CU_ASSERT (c == NULL);


  x = 'J';
  c = set_remove (second, &x);
  CU_ASSERT (c != NULL);
  CU_ASSERT (*c == 'J');
  free (c);

  x = 'E';
  c = set_remove (second, &x);
  CU_ASSERT (c != NULL);
  CU_ASSERT (*c == 'E');
  free (c);

  x = 'Z';
  c = set_remove (second, &x);
  CU_ASSERT (c == NULL);

  x = 'M';
  c = set_remove (second, &x);
  CU_ASSERT (c == NULL);

  x = 'Q';
  c = set_remove (second, &x);
  CU_ASSERT (c == NULL);
}

void
test_set_traverser ()
{
  SetTraverser trav;
  char *c;
  int count;

  count = 0;
  set_traverser_init (&trav, first);
  while ((c = set_traverser_next (&trav)) != NULL)
    {
      if (*c == 'F')
	count++;

      CU_ASSERT (*c == 'B' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'B' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'B' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'B' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'B' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'B' + count);

      count++;
    }
  
  CU_ASSERT (*((char *)set_traverser_first (&trav)) == 'B');
  CU_ASSERT (*((char *)set_traverser_last (&trav)) == 'F');

  CU_ASSERT (*((char *)set_traverser_first (&trav)) == 'B');
  CU_ASSERT (*((char *)set_traverser_last (&trav)) == 'F');

  CU_ASSERT (*((char *)set_traverser_first (&trav)) == 'B');
  CU_ASSERT (*((char *)set_traverser_last (&trav)) == 'F');

  CU_ASSERT (*((char *)set_traverser_first (&trav)) == 'B');
  CU_ASSERT (*((char *)set_traverser_last (&trav)) == 'F');


  count = 0;
  set_traverser_init (&trav, first);
  while ((c = set_traverser_prev (&trav)) != NULL)
    {
      if (*c == 'D')
	count--;

      CU_ASSERT (*c == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);

      count--;
    }

  count = 0;
  set_traverser_init (&trav, second);
  while ((c = set_traverser_next (&trav)) != NULL)
    {
      CU_ASSERT (*c == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'F' + count);

      count++;
    }
  
  CU_ASSERT (*((char *)set_traverser_first (&trav)) == 'F');
  CU_ASSERT (*((char *)set_traverser_last (&trav)) == 'I');

  CU_ASSERT (*((char *)set_traverser_first (&trav)) == 'F');
  CU_ASSERT (*((char *)set_traverser_last (&trav)) == 'I');

  CU_ASSERT (*((char *)set_traverser_first (&trav)) == 'F');
  CU_ASSERT (*((char *)set_traverser_last (&trav)) == 'I');

  CU_ASSERT (*((char *)set_traverser_first (&trav)) == 'F');
  CU_ASSERT (*((char *)set_traverser_last (&trav)) == 'I');


  count = 0;
  set_traverser_init (&trav, second);
  while ((c = set_traverser_prev (&trav)) != NULL)
    {
      CU_ASSERT (*c == 'I' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'I' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'I' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'I' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'I' + count);
      CU_ASSERT (*((char *)set_traverser_cur (&trav)) == 'I' + count);

      count--;
    }
}

void
test_set_comps ()
{
  char c;
  Set *t;
  char i[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};

  c = 'A';
  CU_ASSERT (set_exists (first, &c) == 0);
  CU_ASSERT (set_exists (second, &c) == 0);

  c = 'B';
  CU_ASSERT (set_exists (first, &c) == 1);
  CU_ASSERT (set_exists (second, &c) == 0);

  c = 'C';
  CU_ASSERT (set_exists (first, &c) == 1);
  CU_ASSERT (set_exists (second, &c) == 0);

  c = 'D';
  CU_ASSERT (set_exists (first, &c) == 1);
  CU_ASSERT (set_exists (second, &c) == 0);

  c = 'E';
  CU_ASSERT (set_exists (first, &c) == 0);
  CU_ASSERT (set_exists (second, &c) == 0);

  c = 'F';
  CU_ASSERT (set_exists (first, &c) == 1);
  CU_ASSERT (set_exists (second, &c) == 1);

  c = 'G';
  CU_ASSERT (set_exists (first, &c) == 0);
  CU_ASSERT (set_exists (second, &c) == 1);

  c = 'H';
  CU_ASSERT (set_exists (first, &c) == 0);
  CU_ASSERT (set_exists (second, &c) == 1);

  c = 'I';
  CU_ASSERT (set_exists (first, &c) == 0);
  CU_ASSERT (set_exists (second, &c) == 1);

  c = 'J';
  CU_ASSERT (set_exists (first, &c) == 0);
  CU_ASSERT (set_exists (second, &c) == 0);

  t = set_create (&comp_char);
  CU_ASSERT (set_empty (first) == 0);
  CU_ASSERT (set_empty (second) == 0);
  CU_ASSERT (set_empty (t) == 1);

  set_add (t, &(i[1]));
  set_add (t, &(i[3]));

  CU_ASSERT (set_subset (t, first) == 1);
  CU_ASSERT (set_subset (t, second) == 0);
  CU_ASSERT (set_contains (first, t) == 1);
  CU_ASSERT (set_contains (t, second) == 0);
  CU_ASSERT (set_distinct (t, first) == 0);
  CU_ASSERT (set_distinct (t, second) == 1);

  set_add (t, &(i[7]));

  CU_ASSERT (set_subset (t, first) == 0);
  CU_ASSERT (set_subset (t, second) == 0);
  CU_ASSERT (set_contains (first, t) == 0);
  CU_ASSERT (set_contains (second, t) == 0);
  CU_ASSERT (set_distinct (t, first) == 0);
  CU_ASSERT (set_distinct (t, second) == 0);

  set_remove (t, &(i[1]));
  set_remove (t, &(i[3]));

  CU_ASSERT (set_subset (t, first) == 0);
  CU_ASSERT (set_subset (t, second) == 1);
  CU_ASSERT (set_contains (first, t) == 0);
  CU_ASSERT (set_contains (second, t) == 1);
  CU_ASSERT (set_distinct (t, first) == 1);
  CU_ASSERT (set_distinct (t, second) == 0);

  CU_ASSERT (set_equal (first, t) == 0);
  CU_ASSERT (set_equal (second, t) == 0);

  set_add (t, &(i[5]));
  set_add (t, &(i[6]));
  set_add (t, &(i[8]));

  CU_ASSERT (set_equal (first, t) == 0);
  CU_ASSERT (set_equal (second, t) == 1);

  set_destroy (t, NULL);
  
}

void
test_set_union ()
{
  Set *t, *u;
  char c;

  t = set_union (first, second);
  u = set_union (second, first);

  CU_ASSERT (set_equal (t, u) == 1);
  CU_ASSERT (set_size (t) == 7);
  CU_ASSERT (set_size (u) == 7);

  CU_ASSERT (set_contains (t, u) == 1);
  CU_ASSERT (set_subset (t, u) == 1);

  CU_ASSERT (set_contains (t, first) == 1);
  CU_ASSERT (set_subset (first, t) == 1);
  CU_ASSERT (set_contains (t, second) == 1);
  CU_ASSERT (set_subset (second, t) == 1);

  CU_ASSERT (set_contains (u, first) == 1);
  CU_ASSERT (set_subset (first, u) == 1);
  CU_ASSERT (set_contains (u, second) == 1);
  CU_ASSERT (set_subset (second, u) == 1);

  c = 'B';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'B');
  c = 'C';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'C');
  c = 'D';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'D');
  c = 'F';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'F');
  c = 'G';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'G');
  c = 'H';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'H');
  c = 'I';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'I');
  c = 'J';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'A';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  
  set_destroy (t, NULL);
  set_destroy (u, NULL);
}

void
test_set_intersect ()
{
  Set *t, *u;
  char c;

  t = set_intersect (first, second);
  u = set_intersect (second, first);

  CU_ASSERT (set_equal (t, u) == 1);
  CU_ASSERT (set_size (t) == 1);
  CU_ASSERT (set_size (u) == 1);

  CU_ASSERT (set_distinct (t, u) == 0);

  CU_ASSERT (set_contains (t, u) == 1);
  CU_ASSERT (set_subset (t, u) == 1);

  CU_ASSERT (set_contains (t, first) == 0);
  CU_ASSERT (set_subset (t, first) == 1);
  CU_ASSERT (set_contains (t, second) == 0);
  CU_ASSERT (set_subset (t, second) == 1);

  CU_ASSERT (set_contains (u, first) == 0);
  CU_ASSERT (set_subset (u, first) == 1);
  CU_ASSERT (set_contains (u, second) == 0);
  CU_ASSERT (set_subset (u, second) == 1);

  c = 'B';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'C';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'D';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'F';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'F');
  c = 'G';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'H';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'I';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'J';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'A';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  
  set_destroy (t, NULL);
  set_destroy (u, NULL);

}

void
test_set_difference ()
{
  Set *t, *u;
  char c;

  t = set_difference (first, second);
  u = set_difference (second, first);

  CU_ASSERT (set_equal (t, u) == 0);
  CU_ASSERT (set_size (t) == 3);
  CU_ASSERT (set_size (u) == 3);

  CU_ASSERT (set_contains (t, u) == 0);
  CU_ASSERT (set_subset (t, u) == 0);

  CU_ASSERT (set_contains (t, first) == 0);
  CU_ASSERT (set_subset (t, first) == 1);
  CU_ASSERT (set_contains (t, second) == 0);
  CU_ASSERT (set_subset (t, second) == 0);

  CU_ASSERT (set_contains (u, first) == 0);
  CU_ASSERT (set_subset (u, first) == 0);
  CU_ASSERT (set_contains (u, second) == 0);
  CU_ASSERT (set_subset (u, second) == 1);

  c = 'B';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'B');
  c = 'C';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'C');
  c = 'D';
  CU_ASSERT (*((char *)set_search (t, &c)) == 'D');
  c = 'F';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'G';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'H';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'I';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'J';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  c = 'A';
  CU_ASSERT (((char *)set_search (t, &c)) == NULL);
  
  c = 'B';
  CU_ASSERT (((char *)set_search (u, &c)) == NULL);
  c = 'C';
  CU_ASSERT (((char *)set_search (u, &c)) == NULL);
  c = 'D';
  CU_ASSERT (((char *)set_search (u, &c)) == NULL);
  c = 'F';
  CU_ASSERT (((char *)set_search (u, &c)) == NULL);
  c = 'G';
  CU_ASSERT (*((char *)set_search (u, &c)) == 'G');
  c = 'H';
  CU_ASSERT (*((char *)set_search (u, &c)) == 'H');
  c = 'I';
  CU_ASSERT (*((char *)set_search (u, &c)) == 'I');
  c = 'J';
  CU_ASSERT (((char *)set_search (u, &c)) == NULL);
  c = 'A';
  CU_ASSERT (((char *)set_search (u, &c)) == NULL);

  set_destroy (t, NULL);
  set_destroy (u, NULL);

}

int
make_set_suite (CU_pSuite s)
{
  s = CU_add_suite ("Test Set Suite", 
		    init_test_set_suite, 
		    clean_test_set_suite);
  if (s == NULL)
    {
      return CU_get_error();
    }

  if (CU_add_test (s, "test set_add's", test_set_add) == NULL ||
      CU_add_test (s, "test set_search's", test_set_search) == NULL ||
      CU_add_test (s, "test set_remove's", test_set_remove) == NULL ||
      CU_add_test (s, "test set_comps's", test_set_comps) == NULL ||
      CU_add_test (s, "test set_traverser's", test_set_traverser) == NULL ||
      CU_add_test (s, "test set_union's", test_set_union) == NULL ||
      CU_add_test (s, "test set_intersect's", test_set_intersect) == NULL ||
      CU_add_test (s, "test set_difference's", test_set_difference) == NULL
      )
    {
      return CU_get_error();
    }

  return CU_get_error();
}
